﻿namespace Modbus.Extensions.Enron
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;

    using Device;
    using Utility;


    /// <summary>
    ///     安然Modbus扩展的实用程序扩展。
    /// </summary>
    public static class EnronModbus
    {
        /// <summary>
        ///  读取32位保持寄存器的连续块。
        /// </summary>
        /// <param name="master">主设备对象</param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="startAddress">寄存器起始地址</param>
        /// <param name="numberOfPoints">要读取的寄存器数量</param>
        /// <returns>保持寄存器的数据</returns>
        public static uint[] ReadHoldingRegisters32(this ModbusMaster master, byte slaveAddress, ushort startAddress,
            ushort numberOfPoints)
        {
            if (master == null)
                throw new ArgumentNullException("master");
            ValidateNumberOfPoints(numberOfPoints, 62);

            // read 16 bit chunks and perform conversion
            var rawRegisters = master.ReadHoldingRegisters(slaveAddress, startAddress, (ushort) (numberOfPoints*2));

            return Convert(rawRegisters).ToArray();
        }

        /// <summary>
        ///  读取32位输入寄存器的连续块。
        /// </summary>
        /// <param name="master">主设备对象</param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="startAddress">要读取的寄存器起始地址</param>
        /// <param name="numberOfPoints">要读取的输入寄存器数量</param>
        /// <returns>输入寄存器数据</returns>
        public static uint[] ReadInputRegisters32(this ModbusMaster master, byte slaveAddress, ushort startAddress,
            ushort numberOfPoints)
        {
            if (master == null)
                throw new ArgumentNullException("master");
            ValidateNumberOfPoints(numberOfPoints, 62);

            var rawRegisters = master.ReadInputRegisters(slaveAddress, startAddress, (ushort) (numberOfPoints*2));

            return Convert(rawRegisters).ToArray();
        }

        /// <summary>
        ///  写单个寄存器。
        /// </summary>
        /// <param name="master">主设备对象</param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="registerAddress">需要写入的地址</param>
        /// <param name="value">需要写入的值</param>
        public static void WriteSingleRegister32(this ModbusMaster master, byte slaveAddress, ushort registerAddress,
            uint value)
        {
            if (master == null)
                throw new ArgumentNullException("master");

            master.WriteMultipleRegisters32(slaveAddress, registerAddress, new[] {value});
        }

        /// <summary>
        /// 写入32个保持寄存器数据。
        /// </summary>
        /// <param name="master">主设备对象</param>
        /// <param name="slaveAddress">从设备地址</param>
        /// <param name="startAddress">要写入的起始地址</param>
        /// <param name="data">需要写入的值</param>
        public static void WriteMultipleRegisters32(this ModbusMaster master, byte slaveAddress, ushort startAddress,
            uint[] data)
        {
            if (master == null)
                throw new ArgumentNullException("master");
            if (data == null)
                throw new ArgumentNullException("data");

            if (data.Length == 0 || data.Length > 61)
            {
                throw new ArgumentException(String.Format(CultureInfo.InvariantCulture,
                    "The length of argument data must be between 1 and 61 inclusive."));
            }

            master.WriteMultipleRegisters(slaveAddress, startAddress, Convert(data).ToArray());
        }

        /// <summary>
        /// 将32位寄存器数据转成两个16位数据。
        /// </summary>
        private static IEnumerable<ushort> Convert(uint[] registers)
        {
            foreach (var register in registers)
            {
                // low order value
                yield return BitConverter.ToUInt16(BitConverter.GetBytes(register), 0);

                // high order value
                yield return BitConverter.ToUInt16(BitConverter.GetBytes(register), 2);
            }
        }

        /// <summary>
        ///  将16位寄存器数据转成32位寄存器数据。
        /// </summary>
        private static IEnumerable<uint> Convert(ushort[] registers)
        {
            for (int i = 0; i < registers.Length; i++)
            {
                yield return ModbusUtility.GetUInt32(registers[i + 1], registers[i]);
                i++;
            }
        }

        /// <summary>
        /// 验证数据点数量
        /// </summary>
        /// <param name="numberOfPoints">数据点数量</param>
        /// <param name="maxNumberOfPoints">最大数据点数量</param>
        private static void ValidateNumberOfPoints(ushort numberOfPoints, ushort maxNumberOfPoints)
        {
            if (numberOfPoints < 1 || numberOfPoints > maxNumberOfPoints)
            {
                throw new ArgumentException(String.Format(CultureInfo.InvariantCulture,
                    "Argument numberOfPoints must be between 1 and {0} inclusive.",
                    maxNumberOfPoints));
            }
        }
    }
}
